package info.emm.weiyipro.demo.sdk;


import android.content.Context;
import android.os.Build;
import android.util.ArrayMap;
import android.util.Log;

import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.EglBase;
import org.webrtc.SurfaceViewRenderer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import info.emm.weiyipro.demo.sdk.system.AudioPlayManager;
import wy.base.ActionCallback;
import wy.base.ContextInitialization;
import wy.base.LocalStream;
import wy.base.MediaConstraints;
import wy.base.VideoCapturer;
import wy.base.VideoCodecParameters;
import wy.base.VideoEncodingParameters;
import wy.base.WyError;
import wy.conference.ConferenceClient;
import wy.conference.ConferenceInfo;
import wy.conference.Publication;
import wy.conference.PublicationSettings;
import wy.conference.PublishOptions;
import wy.conference.RemoteStream;
import wy.conference.SubscribeOptions;
import wy.conference.Subscription;
import wy.conference.SubscriptionCapabilities;
import wy.sample.utils.WyVideoCapturer;
import wy.sdk.BuildConfig;

import static wy.base.MediaCodecs.VideoCodec.VP8;


public class WySdk {

    private static final String TAG = WySdk.class.getSimpleName();
    private static WySdk instance;
    private static EglBase rootEglBase;
    private static boolean contextHasInitialized = false;

    private Context appContent;
    private boolean isDebug = BuildConfig.DEBUG;
    private ArrayMap<String, List<String>> videoCodecMap = new ArrayMap<>();
    private ArrayMap<String, Subscription> subMap = new ArrayMap<>();


    private int[] resolution = new int[]{640, 480, 15};
    private WyVideoCapturer localCapturer;
    private LocalStream localStream;
    private Publication publication;
    private String mUserId;
    public static final String USERID = "userId";
    public static final String STREAMTYPE = "streamType";


    public void exitMeeting() {
        videoCodecMap.clear();
        subMap.clear();
        stopPublishStream();
        stopLocalStream();
        ConferenceFactory.destory();
        SingleExecutorCenter.instance().clear();
    }


    private WySdk() {

    }

    public static EglBase getRootEglBase() {
        return rootEglBase;
    }

    public static WySdk instance() {

        if (instance == null) {
            synchronized (WySdk.class) {
                if (instance == null) {
                    instance = new WySdk();
                }
            }
        }
        return instance;
    }

    public Context getAppContent() {
        return appContent;
    }

    public void init(Context weiyiApplication) {

        if (!contextHasInitialized) {
            this.appContent = weiyiApplication;
            rootEglBase = EglBase.create();
            ContextInitialization.create()
                    .setApplicationContext(weiyiApplication)
                    .setVideoHardwareAccelerationOptions(
                            rootEglBase.getEglBaseContext(),
                            rootEglBase.getEglBaseContext())
                    .initialize();
            contextHasInitialized = true;
            AudioPlayManager.getManager().init(weiyiApplication);
        }
    }

    /**
     * 媒体服务器监听
     *
     * @param observer
     */
    public void addObserver(ConferenceClient.ConferenceClientObserver observer) {
        ConferenceFactory.instance().addObserver(observer);
    }

    /**
     * 连接媒体服务器
     *
     * @param medialUrl 媒体服务器地址
     * @param mUserId   用户id  {@link wy.conference.Participant#userId}
     * @param roomId    会议id
     * @param callback
     */
    public void joinRoom(String medialUrl, String mUserId, String roomId, ActionCallback<ConferenceInfo> callback) {
        this.mUserId=mUserId;
        SingleExecutorCenter.instance().run(() -> {

            JSONObject joinBody = new JSONObject();
            try {
                joinBody.put("role", "presenter");
                joinBody.put("userId", mUserId);
                joinBody.put("room", roomId.equals("") ? "" : roomId);
            } catch (JSONException e) {
                e.printStackTrace();
            }

            String uri = medialUrl + "/createToken/";


            String token = HttpUtils.request(uri, "POST", joinBody.toString(), true);
            Log.i(TAG, "onCreate: roomId: " + roomId + "body:" + joinBody.toString() + ",token:" + token);

            ConferenceFactory.instance().join(token, callback);
        });
    }

    public void getParameterByRemoteStream(RemoteStream remoteStream) {
        List<String> videoCodecList = new ArrayList<>();
        SubscriptionCapabilities.VideoSubscriptionCapabilities videoSubscriptionCapabilities
                = remoteStream.extraSubscriptionCapability.videoSubscriptionCapabilities;
        if (videoSubscriptionCapabilities != null) {
            for (VideoCodecParameters videoCodec : videoSubscriptionCapabilities.videoCodecs) {
                videoCodecList.add(videoCodec.name.name());
                videoCodecMap.put(remoteStream.id(), videoCodecList);
            }
        }

        for (PublicationSettings.VideoPublicationSettings videoPublicationSetting :
                remoteStream.publicationSettings.videoPublicationSettings) {
            if (videoCodecMap.containsKey(remoteStream.id())) {
                videoCodecMap.get(remoteStream.id()).add(videoPublicationSetting.codec.name.name());
            } else {
                videoCodecList.add(videoPublicationSetting.codec.name.name());
                videoCodecMap.put(remoteStream.id(), videoCodecList);
            }
        }
    }

    /**
     * 创建本地音视频流
     * 只创建本地视频流{@link LocalStream#LocalStream(VideoCapturer)}
     * 只创建本地音频流{@link LocalStream#LocalStream(MediaConstraints.AudioTrackConstraints)} )}
     * @return
     */
    public LocalStream cteatLocalStream() {

        Log.d(TAG, "cteatLocalStream: ");
        if (localStream != null)
            return localStream;
        localCapturer = WyVideoCapturer.create(resolution[0], resolution[1], resolution[2], true,
                true);
        localStream = new LocalStream(localCapturer, new MediaConstraints.AudioTrackConstraints());
//
        HashMap<String, String> map = new HashMap<>();
        map.put(USERID, mUserId);
        map.put(STREAMTYPE, "av");//音视频，可协商自定义类型


//        localStream = new LocalStream(new MediaConstraints.AudioTrackConstraints());//音频
//        map.put(STREAMTYPE, "audio");//音频


//        localStream=new LocalStream(localCapturer);
//        map.put(STREAMTYPE, "video");//视频
        localStream.setAttributes(map);
        return localStream;

    }

    /**
     * 停止本地音视频采集
     */
    public void stopLocalStream() {
        if (localCapturer != null) {
            localCapturer.dispose();
            localCapturer = null;
            if (localStream != null)
                localStream = null;
        }
    }


    /**
     * 推流
     *
     * @param callback
     */
    public void publishStream(ActionCallback<Publication> callback) {
        SingleExecutorCenter.instance().run(() -> {
            if (localStream == null) {
                if (callback != null)
                    callback.onFailure(new WyError("should create stream before!"));
                return;
            }
            if (publication != null) {
                if (callback != null)
                    callback.onFailure(new WyError("already published !"));
                return;
            }

            ActionCallback<Publication> pCallback = new ActionCallback<Publication>() {
                @Override
                public void onSuccess(Publication o) {
                    publication = o;
                    if (callback != null)
                        callback.onSuccess(o);
                }

                @Override
                public void onFailure(WyError wyError) {
                    if (callback != null)
                        callback.onFailure(wyError);
                }
            };
            ConferenceFactory.instance().publish(localStream, getPublishOptions(), pCallback);
        });

    }

    /**
     * 停止推流
     */
    public void stopPublishStream() {
        SingleExecutorCenter.instance().run(() -> {
            if (publication != null) {
                publication.stop();
                publication = null;
            }
        });
    }

    /**
     * 推流选项
     *
     * @return
     */
    private PublishOptions getPublishOptions() {
        PublishOptions options = null;
        VideoEncodingParameters vp8 = new VideoEncodingParameters(new VideoCodecParameters(
                VP8));


        options = PublishOptions.builder()
                .addVideoParameter(vp8)
                .build();

        return options;
    }

    public void attachLocalStream(SurfaceViewRenderer local_surface) {
        localStream.attach(local_surface);
    }

    public void detachLocalStream(SurfaceViewRenderer local_surface) {
        if (localStream == null)
            return;
        localStream.detach(local_surface);
    }

    /**
     * 切换前后摄像头
     */
    public void switchCamera() {
        if (localCapturer != null) {
            localCapturer.switchCamera();
        }
    }

    /**
     * 重新开始采集视频
     * camera 相机服务被置于后台一段时间后会被系统kill或者摄像头被其他程序占用，so 重新返回界面需要重启采集
     */
    public void restStartCapturer() {
        if (localCapturer != null) {
            localCapturer.changeCaptureFormat(resolution[0], resolution[1], resolution[2]);
        }
    }


    public void sendNotification(String jsonStr, ActionCallback<Void> callback) {
        if (jsonStr == null)
            return;
        Log.i(TAG, "sendMessage: " + jsonStr);
        ConferenceFactory.instance().send(jsonStr, callback);
    }

    /**
     * 发送信令
     * <p>
     * {@link WyNotification}
     * {"opt":"send","id":"","toID":"8SAKytiUSn3FBO5LABGa","name":"testName","body":[{"id":"8SAKytiUSn3FBO5LABGa","userId":"SCM-W09"}]}
     * @param name 信令名称
     * @param toId 发送给toId
     * @param body 发送的内容
     * @param <T>
     */
    public <T> void send(final String name, final String toId, final T body) {
       send(name,toId,body,null);
    }
    /**
     * 发送信令
     * <p>
     * {@link WyNotification}
     * {"opt":"send","id":"","toID":"8SAKytiUSn3FBO5LABGa","name":"testName","body":[{"id":"8SAKytiUSn3FBO5LABGa","userId":"SCM-W09"}]}
     * @param name 信令名称
     * @param toId 发送给toId
     * @param body 发送的内容
     * @param <T>
     */
    public <T> void send(final String name, final String toId, final T body,ActionCallback<Void> callback) {

        SingleExecutorCenter.instance().run(new Runnable() {
            @Override
            public void run() {
                WyNotification notification = new WyNotification<T>();
                notification.setOpt("send");
                notification.setId("");
                notification.setName(name);
                notification.setToID(toId);
                notification.setBody(body);
                sendNotification(new Gson().toJson(notification).toString(), callback);
            }
        });

    }

    /**
     * 拉流
     * 注意线程同步，避免多次重复拉流
     *
     * @param remoteStream
     * @param callback
     */
    public void subscribeStream(RemoteStream remoteStream, ActionCallback<RemoteStream> callback) {
        SingleExecutorCenter.instance().run(new Runnable() {
            @Override
            public void run() {
                Subscription subscription = subMap.get(remoteStream.id());
                if (subscription != null) {
                    if (callback != null)
                        callback.onFailure(new WyError("already sub"));
                    return;
                }
                Log.d(TAG, "subscribeStream: " );
                ActionCallback<Subscription> subCallback = new ActionCallback<Subscription>() {
                    @Override
                    public void onSuccess(Subscription o) {
                        subMap.put(remoteStream.id(), o);
                        if (callback != null)
                            callback.onSuccess(remoteStream);
                    }

                    @Override
                    public void onFailure(WyError wyError) {
                        if (callback != null)
                            callback.onFailure(wyError);
                    }
                };
                ConferenceFactory.instance().subscribe(remoteStream, getSubOption(remoteStream), subCallback);
            }
        });
    }

    /**
     * 拉流选项
     * 根据推流端推流类型来拉取对应的音频/视频/音视频流
     * subAudio 是否开启音频
     * subVideo 是否开启视频
     *
     * @param remoteStream
     * @return
     */
    private SubscribeOptions getSubOption(RemoteStream remoteStream) {
        boolean subAudio = true;
        boolean subVideo = true;


        /**
         * 选择推流端设置的音频编码，一一对应，避免服务端转码，造成服务端性能浪费
         */
        SubscribeOptions.AudioSubscriptionConstraints.Builder audioBuild = SubscribeOptions.AudioSubscriptionConstraints.builder();
        List<PublicationSettings.AudioPublicationSettings> audioCodes = remoteStream.publicationSettings.audioPublicationSettings;
        if (audioCodes != null) {
            for (PublicationSettings.AudioPublicationSettings audioCode : audioCodes) {
                audioBuild.addCodec(audioCode.codec);
            }
        }

        SubscribeOptions options = null;

        /**
         * 同音频
         */
        SubscribeOptions.VideoSubscriptionConstraints.Builder videoBuild = SubscribeOptions.VideoSubscriptionConstraints.builder();
        List<PublicationSettings.VideoPublicationSettings> videoCodes = remoteStream.publicationSettings.videoPublicationSettings;
        if (videoCodes != null) {
            for (PublicationSettings.VideoPublicationSettings videoCode : videoCodes) {
                videoBuild.addCodec(videoCode.codec);
            }
        }

        options = SubscribeOptions.builder(subAudio, subVideo)
                .setAudioOption(audioBuild.build())
                .setVideoOption(videoBuild.build())
                .build();

        return options;
    }

    /**
     * 停止拉流
     *
     * @param remoteStream
     */
    public void stopSubscribe(RemoteStream remoteStream) {
        SingleExecutorCenter.instance().run(() -> {

            Subscription subscription = subMap.get(remoteStream.id());
            if (subscription != null) {
                subscription.stop();
                subscription = null;
                return;
            }

        });
    }
}